using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.IO;
using net.windward.api.csharp;
using System.Net;
using System.Data.Common;
using System.Web;
using Microsoft.SharePoint.WebControls;
using System.Collections;
using net.windward.utils.ado;
using net.windward.tags;
using System.Text.RegularExpressions;
using System.Configuration;
using net.windward.util;
using System.Xml;
using Microsoft.SharePoint.Administration;
using System.Globalization;
using log4net;
using System.Web.UI.WebControls;
using System.Security.Principal;
using Kailua.net.windward.utils.ado.SPList.custom;
using Kailua.net.windward.utils;
using ArrowScheduleCore;
using WindwardArrow.net.windward.arrow.datasource;

namespace ArrowWebParts
{
    /// <summary>
    /// Provides static utility methods and variables for Arrow.
    /// </summary>
    public static class ArrowUtils
    {
        /// <summary>
        /// This is in case we need logging, but we can't get a logger from the caller.  For example, if we can't change the API yet.
        /// </summary>
        private static ILog _log = LogManager.GetLogger(typeof(ArrowUtils));

        #region Constants

        /// <summary>
        /// default text box width for devexpress text boxes
        /// </summary>
        public static readonly Unit DEFAULT_TXTBX_WIDTH = new Unit(160, UnitType.Pixel);
        /// <summary>
        /// "DOCX"
        /// </summary>
        private const string RPT_DOCX = "DOCX";
        /// <summary>
        /// "SML"
        /// </summary>
        private const string RPT_SML = "SML";
        /// <summary>
        /// "PDF"
        /// </summary>
        private const string RPT_PDF = "PDF";
        /// <summary>
        /// "PPTX"
        /// </summary>
        private const string RPT_PPTX = "PPTX";
        /// <summary>
        /// "RTF"
        /// </summary>
        private const string RPT_RTF = "RTF";
        /// <summary>
        /// "WML"
        /// </summary>
        private const string RPT_WML = "WML";
        /// <summary>
        /// "XLS"
        /// </summary>
        private const string RPT_XLS = "XLS";
        /// <summary>
        /// "XLSX"
        /// </summary>
        private const string RPT_XLSX = "XLSX";
        /// <summary>
        /// "HTML"
        /// </summary>
        private const string RPT_HTML = "HTML";

#if SP2007
        /// <summary>
        /// <para>Relative path to arrow images.</para>
        /// <para>\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\1033\IMAGES\Arrow</para>
        /// </summary>
        private const string IMG_DIR = "\\Common Files\\microsoft shared\\Web Server Extensions\\12\\TEMPLATE\\LAYOUTS\\1033\\IMAGES\\Arrow";
#endif

#if SP2010
        /// <summary>
        /// <para>Relative path to arrow images.</para>
        /// <para>\Common Files\microsoft shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\1033\IMAGES\Arrow</para>
        /// </summary>
        private const string IMG_DIR = "\\Common Files\\microsoft shared\\Web Server Extensions\\14\\TEMPLATE\\LAYOUTS\\1033\\IMAGES\\Arrow";
#endif

        #endregion Constants

        #region CssStyles
        //This is where we have all the getters for the CSS styles to be used throughout Arrow.
        public static String css_pageTitle()
        {
            return "ms-pagetitle";
        }
        public static String css_tableCell()
        {
            return ".ms-formlabel";
        }
        #endregion 

        #region regularStyles
        public static Style headerStyle()
        {
            Style headerStyle = new Style();
            headerStyle.Font.Name = "Verdana";
            headerStyle.Font.Size = FontUnit.Parse("14pt");
            return headerStyle;
        }
        public static Style errorStyle()
        {
            Style errorStyle = new Style();
            errorStyle.Font.Name = "Verdana";
            errorStyle.Font.Size = FontUnit.Parse("12pt");
            errorStyle.Font.Bold = true;
            return errorStyle;
        }
        public static Style normalBoldStyle()
        {
            Style normalBoldStyle = new Style();
            normalBoldStyle.Font.Name = "Verdana";
            normalBoldStyle.Font.Size = FontUnit.Parse("12pt");
            normalBoldStyle.Font.Bold = true;
            return normalBoldStyle;
        }
        #endregion

        #region String Utils

        public static string trim(string toTrim)
        {
            return toTrim.Trim();
        }
        /// <summary>
        /// Gets the substring after the last '.' in the string, template.
        /// </summary>
        /// <param name="templateFilename"></param>
        /// <returns></returns>
        public static string getTemplateExtension(string templateFilename)
        {
            string ext = templateFilename.Substring((templateFilename.LastIndexOf(".")) + 1);
            return ext.ToLower();
        }
        /// <summary>
        /// Indicates if the extension string is a Windward template extension like .docx or .rtf.
        /// </summary>
        /// <param name="ext"></param>
        /// <returns></returns>
        public static bool IsTemplateExtension(string ext)
        {
            if (string.IsNullOrEmpty(ext))
                return false;

            return (
                "docx".Equals(ext.ToLower()) ||
                "rtf".Equals(ext.ToLower()) ||
                "xml".Equals(ext.ToLower()) ||
                "xlsx".Equals(ext.ToLower()) ||
                "pptx".Equals(ext.ToLower()) ||
                "html".Equals(ext.ToLower())
                );
        }
        /// <summary>
        /// Returns true if the extension is a supported windward template format.
        /// </summary>
        /// <param name="extension"></param>
        /// <returns></returns>
        private static bool IsValidInputFormat(string extension)
        {
            string ext = extension.ToLower();
            if ("rtf".Equals(ext) || "pptx".Equals(ext) || "docx".Equals(ext) || "xlsx".Equals(ext) || "xml".Equals(ext)
                || "html".Equals(ext) || "sml".Equals(ext) || "xlsm".Equals(ext))
                return true;
            return false;
        }
        
        public static string htmlEncode(string toEncode)
        {
            return HttpUtility.HtmlEncode(toEncode);
        }
        
        /// <summary>
        /// Validates a report name string and returns true if the string is valid, false if not.
        /// </summary>
        /// <param name="reportName"></param>
        /// <returns></returns>
        public static bool ValidateReportName(string reportName)
        {
            // from joel oleson: http://blogs.msdn.com/joelo/archive/2007/06/27/file-name-length-size-and-invalid-character-restrictions-and-recommendations.aspx
            // Invalid characters: " # % & * : < > ? \ / { | } ~
            // Cannot be longer than 128 characters
            // You cannot use the period character consecutively in the middle of a file name
            // You cannot use the period character at the end of a file name
            // You cannot start a file name with the period character
            if (reportName == null)
                return false;
            if (reportName.Length <= 0 || reportName.Length > 128)
                return false;
            if (spFilenameContainsInvalidCharacters(reportName))
                return false;
            if (reportName.Contains(".."))
                return false;
            if (reportName.StartsWith("."))
                return false;
            if (reportName.EndsWith("."))
                return false;

            return true;
        }
        /// <summary>
        /// Returns true if the filename contains any bad characters
        /// </summary>
        /// <param name="reportName"></param>
        /// <returns></returns>
        public static bool spFilenameContainsInvalidCharacters(string reportName)
        {
            // from joel oleson: http://blogs.msdn.com/joelo/archive/2007/06/27/file-name-length-size-and-invalid-character-restrictions-and-recommendations.aspx
            // Invalid characters: " # % & * : < > ? \ / { | } ~
            return
                reportName.Contains("\"") ||
                reportName.Contains("#") ||
                reportName.Contains("%") ||
                reportName.Contains("&") ||
                reportName.Contains("*") ||
                reportName.Contains(":") ||
                reportName.Contains("<") ||
                reportName.Contains(">") ||
                reportName.Contains("?") ||
                reportName.Contains("\\") ||
                reportName.Contains("/") ||
                reportName.Contains("{") ||
                reportName.Contains("|") ||
                reportName.Contains("}") ||
                reportName.Contains("~");
        }
        /// <summary>
        /// Gets the name of the dir from the full path.
        /// Everything after the last slash, for example, dir in /root/dir.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string SubstringAfterLastSlash(string path)
        {
            int indName = path.LastIndexOf('/') + 1;
            string name = path.Substring(indName);
            return name;
        }
        /// <summary>
        /// Gets the location part out of the path.  everything through the last slash.
        /// for example, /root/ in /root/dir
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string SubstringThroughLastSlash(string path)
        {
            int lenLoc = path.LastIndexOf('/') + 1;
            if (lenLoc == 0)
                lenLoc = path.Length;
            string location = path.Substring(0, lenLoc);
            return location;
        }
        /// <summary>
        /// Builds a display string for a folder path, eg, Site > Lib > Folder.
        /// This is SPContext dependent.
        /// </summary>
        /// <param name="folderInfo"></param>
        /// <returns></returns>
        public static string BuildFolderPathDisplayString(SPFileSelectorFolderInfo folderInfo)
        {
            if (!folderInfo.IsSPFolder || string.IsNullOrEmpty(folderInfo.WebGuid))
                return "None Selected";

            StringBuilder sb = new StringBuilder();
            SPFolder folder;
            Guid webID;
            using (SPWeb web = SPContext.Current.Site.OpenWeb(new Guid(folderInfo.WebGuid)))
            {
                sb.Append(web.Title);
                webID = web.ID;
                folder = web.GetFolder(folderInfo.Url);
            }
            StringBuilder sbFolderPath = new StringBuilder();
            SPDocumentLibrary lib = ArrowUtils.getDocLibObject(webID, folder.ParentListId);
            while (true)
            {
                sbFolderPath.Insert(0, " > " + folder.Name);
                if (folder.UniqueId == lib.RootFolder.UniqueId)
                    break;
                folder = folder.ParentFolder;
            }
            sb.Append(sbFolderPath.ToString());
            return sb.ToString();
        }
        /// <summary>
        /// <para>Gets a filename extension such as ".docx" based on the report output format.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="rptFormat"></param>
        /// <returns></returns>
        public static string GetRptFileExtension(string rptFormat)
        {
            switch (rptFormat)
            {
                case RPT_DOCX:
                    return ".docx";
                case RPT_HTML:
                    return ".html";
                case RPT_PDF:
                    return ".pdf";
                case RPT_PPTX:
                    return ".pptx";
                case RPT_RTF:
                    return ".rtf";
                case RPT_SML:
                    return ".xml";
                case RPT_WML:
                    return ".xml";
                case RPT_XLS:
                    return ".xls";
                case RPT_XLSX:
                    return ".xlsx";
                default:
                    throw new ArgumentException("Invalid report format: " + rptFormat);
            }
        }
        #endregion String Utils

        #region SPObject_Getters

        //SPSite has the SiteCollection which contains all of the team sites
        //for example 'Development' and 'Marketing' team sites are held inside of an SPSite Object
        //where 'Development' and 'Marketing' are SPWeb Objects.

        public static SPSite getRootSPSite()
        {
            return SPContext.Current.Site;
        }

        /// <summary>
        /// <para>Gets a SharePoint web site object from its Guid.</para>
        /// <para>Uses SPContext.</para>
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        public static SPWeb getSPWeb(string guid)
        {
            return getSPWeb(new Guid(guid));
        }

        /// <summary>
        /// <para>Gets a SharePoint web site object from its Guid.</para>
        /// <para>Uses SPContext.</para>
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        public static SPWeb getSPWeb(Guid guid)
        {
            SPWeb web = GetSPWebCollectionFromCurrentSite()[guid]; // don't want to return this spweb since it has admin privileges
            return getRootSPSite().OpenWeb(web.ID);
        }

        /// <summary>
        /// <para>Gets an SPDocumentLibrary from the specified SPWeb.</para>
        /// <para>Uses SPContext.</para>
        /// </summary>
        /// <param name="webGUID">guid of the SPWeb</param>
        /// <param name="docLibGuid">guid of the doc lib</param>
        /// <returns></returns>
        public static SPDocumentLibrary getDocLibObject(string webGUID, string docLibGUID)
        {
            SPWeb teamSite = getSPWeb(webGUID);
            return (SPDocumentLibrary)teamSite.Lists[new Guid(docLibGUID)];
        }
        /// <summary>
        /// <para>Gets an SPDocumentLibrary from the specified SPWeb.</para>
        /// <para>Uses SPContext.</para>
        /// </summary>
        /// <param name="webGUID">guid of the SPWeb</param>
        /// <param name="docLibGuid">guid of the doc lib</param>
        /// <returns></returns>
        public static SPDocumentLibrary getDocLibObject(Guid webGUID, Guid docLibGuid)
        {
            SPWeb teamSite = getSPWeb(webGUID);
            return (SPDocumentLibrary)teamSite.Lists[docLibGuid];
        }

        public static SPFile getFileFromDocLib(SPDocumentLibrary docLib, string fileGUID)
        {
            return getFileFromDocLib(docLib, new Guid(fileGUID));
        }
        public static SPFile getFileFromDocLib(SPDocumentLibrary docLib, Guid fileGUID)
        {
            return docLib.GetItemByUniqueId(fileGUID).File;
        }

        public static SPFile getFileFromDocLib_usingID(SPDocumentLibrary docLib, int id)
        {
            return GetSPFileFromDocLibUsingID(docLib, id);
        }

        /// <summary>
        /// Gets the file associated with the specified guid from the specified document library.
        /// ArgumentException is thrown if no file is associated with the specified guid.
        /// </summary>
        /// <param name="docLib">the document library to get the file from</param>
        /// <param name="guid">the guid of the file</param>
        public static SPFile GetSPFileFromDocLib(SPDocumentLibrary docLib, string guid)
        {
            Guid fileId = new Guid(guid);
            return docLib.GetItemByUniqueId(fileId).File;
        }

        /// <summary>
        /// Gets a file from a document library using the ID field of the file's list item.
        /// </summary>
        /// <param name="docLib"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static SPFile GetSPFileFromDocLibUsingID(SPDocumentLibrary docLib, int id)
        {
            return docLib.GetItemById(id).File;
        }

        /// <summary>
        /// Gets the collection of all the sites in the site collection.
        /// bugbug;  Note that this does not check against current user permissions (which it really really should);  bandaid code first
        /// </summary>
        /// <param name="site">A SharePoint site collection</param>
        /// <returns>The collection of all the sites in the site collection</returns>
        public static SPWebCollection GetSPWebCollectionFromSite(SPSite site)
        {
            SPUserToken token = SPContext.Current.Web.CurrentUser.UserToken;
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite siteElev = new SPSite(site.Url))
                    token = siteElev.SystemAccount.UserToken;
            });
            SPSite elevatedSiteCollection = new SPSite(site.ID, token);
            return elevatedSiteCollection.AllWebs;
        }

        /// <summary>
        /// Gets the collection of all the sites in the current site collection (so this is context specific).
        /// </summary>
        /// <returns> Collection of all the sites in the current site collection</returns>
        public static SPWebCollection GetSPWebCollectionFromCurrentSite()
        {
            return GetSPWebCollectionFromSite(SPContext.Current.Site);
        }
        #endregion SPObject_Getters

        #region URL_Getters
        /// <summary>
        /// <para>Gets the HttpContext.Current.Request.Url.</para>
        /// <para>Requires HttpContext.</para>
        /// </summary>
        /// <returns></returns>
        public static string getHTTPContextURL()
        {
            return System.Web.HttpContext.Current.Request.Url.ToString();
        }

        public static string getAbsPathFromSPFile(SPFile file)
        {
            string relativePath = file.ServerRelativeUrl;
            return getRootSPSite().MakeFullUrl(relativePath);
        }

        public static string makeURL(string DisplayTitle, string url)
        {
            return "<a href=\"" + url + "\">" + DisplayTitle + "</a>";
        }
        /// <summary>
        /// <para>Gets the full URL of the specified file.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="webGuid"></param>
        /// <param name="fileGuid"></param>
        /// <returns></returns>
        public static string GetAbsolutePath(Guid webGuid, Guid fileGuid)
        {
            SPFile file = GetSPFileFromSPWeb(webGuid, fileGuid);
            return SPContext.Current.Site.MakeFullUrl(file.ServerRelativeUrl);
        }
        /// <summary>
        /// gets the server relative URL of the specified SPFile.
        /// </summary>
        /// <param name="siteGuid">guid of site containing the file</param>
        /// <param name="docLibGuid">guid of doc lib containing the file</param>
        /// <param name="fileGuid">guid of the file</param>
        /// <returns>the file's server-relative URL</returns>
        public static string GetRelativePath(Guid siteGuid, Guid docLibGuid, Guid fileGuid)
        {
            SPDocumentLibrary fileLib = ArrowUtils.getDocLibObject(siteGuid, docLibGuid);
            SPFile file = getFileFromDocLib(fileLib, fileGuid);
            return file.ServerRelativeUrl;
        }
        #endregion

        #region Windward ReportGen Functions
        /// <summary>
        /// <para>Generates a DisplayReport URL.</para>
        /// <para>Requires HTTPContext</para>
        /// </summary>
        /// <param name="rd"></param>
        /// <param name="frameworkPath"></param>
        /// <param name="outputFormat"></param>
        /// <param name="variableValues"></param>
        /// <returns></returns>
        public static string generateUrl(ReportDefinition rd, string frameworkPath, string outputFormat, string[] variableValues, string reportName)
        {
            int numVars = variableValues == null ? 0 : variableValues.Length;

            string url = ArrowUtils.getHTTPContextURL();

            url = url.Substring(0, url.LastIndexOf("/") + 1);

            url += "_layouts/DisplayReport.aspx" +
                "?rptDef=" + frameworkPath +
                "&format=" + outputFormat;
            if (reportName != null && reportName.Trim().Length > 0)
                url += "&rptName=" + reportName.Trim();
            url += "&numvars=" + numVars;

            for (int i = 0; i < numVars; i++)
            {
                url += "&var" + i.ToString() + "=" + variableValues[i];
            }
            return url;
        }
        /// <summary>
        /// <para>Generates a DisplayReport URL using the specified base URL instead of the HttpContext.Current.Request.Url.</para>
        /// <para>Does not require HttpContext.</para>
        /// </summary>
        /// <param name="baseUrl"></param>
        /// <param name="rd"></param>
        /// <param name="frameworkPath"></param>
        /// <param name="outputFormat"></param>
        /// <param name="variableValues"></param>
        /// <returns></returns>
        public static string generateUrl(string baseUrl, ReportDefinition rd, string frameworkPath, string outputFormat, string[] variableValues, string reportName)
        {
            int numVars = variableValues == null ? 0 : variableValues.Length;

            string url = baseUrl + "/_layouts/DisplayReport.aspx" +
                "?rptDef=" + frameworkPath +
                "&format=" + outputFormat;
            if (reportName != null && reportName.Trim().Length > 0)
                url += "&rptName=" + reportName.Trim();
            url += "&numvars=" + numVars;

            for (int i = 0; i < numVars; i++)
            {
                url += "&var" + i.ToString() + "=" + variableValues[i];
            }
            return url;
        }
        /// <summary>
        /// <para>Generates the specified report to the specified location, and returns the absolute path 
        /// to the generated report.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="rptDef"></param>
        /// <param name="dataDefArray"></param>
        /// <param name="outputLibrary"></param>
        /// <param name="reportName"></param>
        /// <param name="outFormat"></param>
        /// <param name="variableValues"></param>
        /// <param name="outputFolder"></param>
        /// <returns>the absolute path to the generated report</returns>
        public static string generateReport(ReportDefinition rptDef, DataDefinition[] dataDefArray, SPDocumentLibrary outputLibrary, string reportName,
                                    string outFormat, string[] variableValues, SPFolder outputFolder)
        {
            if (_log.IsDebugEnabled)
                _log.Debug("Generating report.");

            // get the template stream
            SPWeb site = outputLibrary.ParentWeb;
            string templatePath = rptDef.template;
            Stream templateStream = GetSPFileStream(templatePath);

            // get the report stream
            Stream reportStream = new MemoryStream();

            // get a report gen
            Report reportGen = CreateReportGen(outFormat, templateStream, reportStream);
            
            // get extension
            string extension = GetRptFileExtension(outFormat);

            // initialize the report gen
            Report.Init();
            reportGen.ProcessSetup();

            // map parameters
            KeyPairDictionary map = CreateVarMap(rptDef, variableValues);
            ArrowUtils.AddCurrentUserInfoToReportVars(map);

            // process the data
            ProcessReportGenDataSources(reportGen, rptDef, dataDefArray, map, _log);

            // finish processing the report
            reportGen.ProcessComplete();
            reportStream.Flush();
            reportGen.Dispose();

            // write the report to the report doc lib
            SPDocumentLibrary reportDocLib = outputLibrary;
            string reportLibRelPath = reportDocLib.RootFolder.ServerRelativeUrl;
            if (outputFolder != null)
            {
                reportLibRelPath = outputFolder.ServerRelativeUrl;
            }
            string reportLibPath = SPContext.Current.Site.MakeFullUrl(reportLibRelPath);
            string reportPath = reportLibPath + "/" + reportName + extension;

            site.AllowUnsafeUpdates = true;
            site.Files.Add(reportPath, reportStream, true);
            site.AllowUnsafeUpdates = false;

            if (_log.IsDebugEnabled)
                _log.Debug("Finished generating report.");

            return reportPath;
        }
        /// <summary>
        /// Creates a Report object based on the specified output format.
        /// </summary>
        /// <param name="templateStream">The report template.</param>
        /// <returns></returns>
        internal static Report CreateReportGen(string rptFormat, Stream templateStream)
        {
            switch (rptFormat)
            {
                case RPT_DOCX:
                    return new ReportDocx(templateStream);
                case RPT_PDF:
                    return new ReportPdf(templateStream);
                case RPT_PPTX:
                    return new ReportPptx(templateStream);
                case RPT_RTF:
                    return new ReportRtf(templateStream);
                case RPT_SML:
                    return new ReportSpreadML(templateStream);
                case RPT_WML:
                    return new ReportWordML(templateStream);
                case RPT_XLS:
                    return new ReportXls(templateStream);
                case RPT_XLSX:
                    return new ReportXlsx(templateStream);
                case RPT_HTML:
                    ReportHtml reportGen = new ReportHtml(templateStream);
                    reportGen.CssType = ReportHtml.CSS.INCLUDE;
                    reportGen.SetImagePath(Environment.GetEnvironmentVariable("ProgramFiles") + IMG_DIR, "1033/images/arrow", "wr_");
                    return reportGen;
                default:
                    throw new ArgumentException("Invalid report format: " + rptFormat);
            }
        }
        /// <summary>
        /// <para>Creates a Report object based on the report output format.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="rptFormat">Report output format.</param>
        /// <param name="templateStream">Template Stream.</param>
        /// <param name="reportStream">Report output Stream.</param>
        /// <returns></returns>
        internal static Report CreateReportGen(string rptFormat, Stream templateStream, Stream reportStream)
        {
            switch (rptFormat)
            {
                case RPT_DOCX:
                    return new ReportDocx(templateStream, reportStream);
                case RPT_HTML:
                    return new ReportHtml(templateStream, reportStream);
                case RPT_PDF:
                    return new ReportPdf(templateStream, reportStream);
                case RPT_PPTX:
                    return new ReportPptx(templateStream, reportStream);
                case RPT_RTF:
                    return new ReportRtf(templateStream, reportStream);
                case RPT_SML:
                    return new ReportSpreadML(templateStream, reportStream);
                case RPT_WML:
                    return new ReportWordML(templateStream, reportStream);
                case RPT_XLS:
                    return new ReportXls(templateStream, reportStream);
                case RPT_XLSX:
                    return new ReportXlsx(templateStream, reportStream);
                default:
                    throw new ArgumentException("Invalid report format: " + rptFormat);
            }
        }

        /// <summary>
        /// <para>Gets a report definition from the specified file.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="file">an SPFile containing a template or report definition</param>
        /// <param name="absolutePath">the full URL to the file</param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        public static ReportDefinition GetReportDefFromFile(SPFile file, string absolutePath, out string errorMessage, Guid siteGuid)
        {
            errorMessage = string.Empty;
            
            // get the file guid used throughout this method
            Guid fileGuid = file.UniqueId;

            // if it's a report def, just load it and return the object
            string filename = file.Name;
            string extension = filename.Substring(filename.LastIndexOf('.') + 1);
            if ("wrf".Equals(extension) || "reportdef".Equals(extension))
                return new ReportDefinition(absolutePath);

            // if it's a valid template extension, process it as a template, and create a report def
            if (IsValidInputFormat(extension))
            {
                //Check to see if you get anything back in the first place.
                // 1. Valid input format (above)
                // 2. Test connection to file
                //bugbug: something looks wrong here; removing because we already have the SPFile
                #region old way
                //try
                //{
                //    MemoryStream testFile = GetSPFileStream(siteGuid, fileGuid);
                //}
                //catch (FileNotFoundException e)
                //{
                //    ArrowUtils.LogFatalException(_log, e);
                //    errorMessage = "Error: Unable to Access template file";
                //    return null;
                //}
                #endregion old way

                // 3. Datasource reference exists
                //bugbug: old way; TemplateMetrics tm = GetTemplateMetrics(siteGuid, fileGuid);
                MemoryStream ms = ArrowUtils.GetSPFileStream(absolutePath);
                TemplateMetrics tm = Report.GetMetrics(ms);
                if (tm == null)
                {
                    errorMessage = string.Format("Error: {0} is not a template.", filename);
                    return null;
                }

                // report definition variables - these will be filled with data from the template
                String title;
                String description;
                String templateUrl;
                String[] dataSourceNameArray = new String[tm.Datasources.Length];
                String[] variableNameArray = new String[tm.Vars.Length];
                String[] variableRequiredArray = new String[tm.Vars.Length];
                String[] variableTypeArray = new String[tm.Vars.Length];
                String[] variableDescriptionArray = new String[tm.Vars.Length];
                String[] variableDefaultArray = new String[tm.Vars.Length];
                String[] variableSelectCountArray = new String[tm.Vars.Length];
                String[] variableSelectAllArray = new String[tm.Vars.Length];
                String[] variableSelectValueArray = new String[tm.Vars.Length];
                String[] variableDataNameArray = new String[tm.Vars.Length];

                // set information for ReportDefinition
                SPFile templateFile = file;
                title = templateFile.Name;
                description = string.Empty;
                //bugbug: old way - we already have the absolute path; templateUrl = GetAbsolutePath(siteGuid, fileGuid);
                templateUrl = absolutePath;
                dataSourceNameArray = tm.Datasources;

                // retrieve DocGlobal, if any

                // process DocGlobal
                DataDefinition[] dataDefArray = new DataDefinition[0];
                List<DataDefinition> defs = new List<DataDefinition>();
                // create the data def list
                foreach (IDictionary<string, string> dsp in tm.TemplateDatasources)
                {
                    if (dsp["full-type"].Equals("AdoDataSourceInfo"))
                    {
                        //ProfileXml();
                        string providerClass = dsp["provider-class"];
                        if (dsp.ContainsKey("connection-string"))
                        {
                            WrVendor vendor = WrProviderFactories.CreateProvider(providerClass);
                            defs.Add(new DataDefinition(null, null, providerClass, dsp["connection-string"], vendor.GUID, vendor.Name));
                        }
                        else
                        {
                            string providerName = dsp["provider-name"];
                            WrVendor vendor = WrProviderFactories.CreateProvider(providerName, null, providerClass);
                            WrCredentials creds;
                            if (dsp.ContainsKey("windows-identity"))
                                creds = new WrCredentials();
                            else
                                creds = new WrCredentials(dsp["username"], dsp["password"]);
                            string connStr = vendor.ConnectionString(dsp["server"], dsp["database"], creds, true);
                            defs.Add(new DataDefinition(null, null, providerClass, connStr, vendor.GUID, providerName));
                        }
                        //DataDefinition(string _title, string _description, string _factoryClass, string _connStr, string _vendorGuid, string _vendorName)
                    }
                    else if (dsp["full-type"].Equals("CamlDataSource"))
                    {
                        // defined in WrSPListVendor2
                        string vendorGuid = "61A1EDE2-5216-420e-BA87-5EEE3430128C";
                        string providerName = "SharePoint List";
                        // note: providerClass not used for splist; see WrProviderFactories.CreateProvider()
                            
                        if (dsp.ContainsKey("connection-string"))
                            defs.Add(new DataDefinition(null, null, "", dsp["connecion-string"], vendorGuid, providerName));
                        else
                        {
                            WrVendor vendor = WrProviderFactories.CreateProvider(providerName, null, "");
                            WrCredentials creds;
                            if (dsp.ContainsKey("windows-identity"))
                                creds = new WrCredentials();
                            else
                                creds = new WrCredentials(dsp["username"], dsp["password"]);
                            string connStr = vendor.ConnectionString(dsp["server"], dsp["database"], creds, true);
                            defs.Add(new DataDefinition(null, null, "", connStr, vendorGuid, providerName));
                        }
                    }
                    else if (dsp["full-type"].Equals("XmlDataSourceInfo"))
                    {
                        //bugbug: are these values arbitrary or required? where are they documented?
                        string url = dsp["url"];
                        defs.Add(new DataDefinition(null, null, "xml", url, "BEE5484C-5CEC-41c2-A8E3-9919317D1432", "XML (file or url)"));
                    }
                }
                dataDefArray = defs.ToArray();

                // create the var list
                if (tm.TemplateVariables.Count > 0)
                {
                    // set tVars
                    IList<TemplateVariable> tVars = tm.TemplateVariables;

                    for (int i = 0; i < tVars.Count; i++)
                    {
                        variableNameArray[i] = tVars[i].Name;
                        variableRequiredArray[i] = tVars[i].Required.ToString();
                        bool hasDefaultValue = tVars[i].DefaultValues.Length > 0 && tVars[i].DefaultValues[0] != null && tVars[i].DefaultValues[0].Value != null;
                        if (!hasDefaultValue)
                            variableDefaultArray[i] = "";//bugbug
                        else
                            variableDefaultArray[i] = tVars[i].DefaultValues[0].Value.ToString();
                        switch (tVars[i].VarType)
                        {
                            case TemplateVariable.VAR_TYPE.CURRENCY:
                                variableTypeArray[i] = "CURRENCY";
                                break;
                            case TemplateVariable.VAR_TYPE.DATE:
                                variableTypeArray[i] = "DATE";
                                break;
                            case TemplateVariable.VAR_TYPE.INTEGER:
                                variableTypeArray[i] = "INTEGER";
                                break;
                            case TemplateVariable.VAR_TYPE.FLOAT:
                                variableTypeArray[i] = "FLOAT";
                                break;
                            case TemplateVariable.VAR_TYPE.SELECT:
                                variableTypeArray[i] = "SELECT";
                                variableSelectCountArray[i] = tVars[i].Dataset.SelectCount;
                                variableSelectAllArray[i] = tVars[i].Dataset.Select;
                                variableSelectValueArray[i] = tVars[i].Dataset.SelectCheck;
                                variableDataNameArray[i] = tVars[i].Datasource;
                                break;
                            case TemplateVariable.VAR_TYPE.TEXT:
                                variableTypeArray[i] = "TEXT";
                                break;
                        }
                        variableDescriptionArray[i] = tVars[i].Description;
                    }
                }

                // create the report def object
                ReportDefinition rd = new ReportDefinition(title, description, templateUrl, dataSourceNameArray, dataDefArray, variableNameArray,
                                                           variableRequiredArray, variableTypeArray, variableDescriptionArray, variableDefaultArray,
                                                           variableSelectCountArray, variableSelectAllArray, variableSelectValueArray,
                                                           variableDataNameArray);
                return rd;
            }
            else
            {
                errorMessage = "Error: Invalid file extension";
                return null;
            }
        }

        public static ReportDefinition GetReportDefFromFile(string fullUrl, out string errorMessage)
        {
            SPFile file = GetSPFile(fullUrl);
            Guid fileGuid = file.UniqueId;
            Guid docLibGuid = file.Item.ParentList.ID;
            Guid siteGuid = file.Item.Web.ID;
            return GetReportDefFromFile(siteGuid, fileGuid, out errorMessage);
        }
        /// <summary>
        /// <para>Gets a ReportDefinition from the specified reportdef or windward template file.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="siteGuid">guid of site containing the file</param>
        /// <param name="docLibGuid">guid of doc lib containing the file</param>
        /// <param name="fileGuid">guid of the file</param>
        /// <param name="errorMessage">error message out-parameter</param>
        /// <returns>the ReportDefinition</returns>
        public static ReportDefinition GetReportDefFromFile(Guid siteGuid, Guid fileGuid, out string errorMessage)
        {
            // get the file - it should be a template or report def
            SPFile file = GetSPFileFromSPWeb(siteGuid, fileGuid);
            string absolutePath = GetAbsolutePath(siteGuid, fileGuid);

            return GetReportDefFromFile(file, absolutePath, out errorMessage, siteGuid);
        }
        /// <summary>
        /// <para>Gets the template metrics from the specified file.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="siteGuid"></param>
        /// <param name="fileGuid"></param>
        /// <returns></returns>
        public static TemplateMetrics GetTemplateMetrics(Guid siteGuid, Guid fileGuid)
        {
            Stream fs = ArrowUtils.GetSPFileStream(siteGuid, fileGuid);
            return Report.GetMetrics(fs);
        }
        #endregion Windward ReportGen Functions

        #region file utils
        /// <summary>
        /// <para>Gets a MemoryStream for the file specified by fullUrl. FileNotFoundException
        /// will be thrown if no SPFile instance is associated with the specified url.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="fullUrl">the full url of the file</param>
        /// <returns>a MemoryStream for the file specified by fullUrl</returns>
        public static MemoryStream GetSPFileStream(string fullUrl)
        {
            return new MemoryStream(GetSPFile(fullUrl).OpenBinary(), false);
        }
        /// <summary>
        /// <para>Gets the SPFile specified by the fullUrl. FileNotFoundException
        /// will be thrown if no SPFile instance is associated with the specified url.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="fullUrl">the full url of the file</param>
        /// <returns>the SPFile specified by the fullUrl</returns>
        public static SPFile GetSPFile(string fullUrl)
        {
            SPFile spFile = null;
            using (SPSite siteCollection = new SPSite(fullUrl))
            {
                using (SPWeb web = siteCollection.OpenWeb())
                {
                    spFile = web.GetFile(fullUrl);
                }
            }
            if (spFile == null)
                throw new FileNotFoundException("The file at " + fullUrl + " resulted in a null file reference.");
            return spFile;
        }
        /// <summary>
        /// <para>Gets a MemoryStream for the file specified by fullUrl.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="siteGuid">guid of the file's parent SPWeb</param>
        /// <param name="docLibGuid">guid of the doc lib containing the file</param>
        /// <param name="fileGuid">the file's guid</param>
        /// <returns>a MemoryStream of the file</returns>
        public static MemoryStream GetSPFileStream(Guid siteGuid, Guid fileGuid)
        {
            SPWeb site = SPContext.Current.Site.OpenWeb(siteGuid);
            SPFile file = site.GetFile(fileGuid);
            MemoryStream templateStream = new MemoryStream(file.OpenBinary(), false);
            return templateStream;
        }

        /// <summary>
        /// Creates and adds a file to a document library using a string as the file contents.
        /// The string is encoded in using System.Text.UTF8Encoding.
        /// </summary>
        /// <param name="fileContents">the contents of the file</param>
        /// <param name="filename">the name of the file, eg, file.extension</param>
        /// <param name="libGuid">guid of the document library</param>
        /// <param name="site">site containing the document library</param>
        public static void CreateAndAddFileFromString(string fileContents, string filename, string libGuid, SPWeb site)
        {
            CreateAndAddFileFromString(fileContents, filename, libGuid, site, null);
        }

        /// <summary>
        /// Creates and adds a file to a document library using a string as the file contents.
        /// The string is encoded using System.Text.UTF8Encoding.
        /// </summary>
        /// <param name="fileContents">the contents of the file</param>
        /// <param name="filename">the name of the file, eg, file.extension</param>
        /// <param name="libGuid">guid of the document library</param>
        /// <param name="site">site containing the document library</param>
        /// <param name="outFolderGuid">guid of the folder (may be null)</param>
        public static void CreateAndAddFileFromString(string fileContents, string filename, string libGuid, SPWeb site, string sOutFolderGuid)
        {
            //fileContents = "test";
            // generate wrf file stream from string
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
            byte[] fileContentsBytes = encoding.GetBytes(fileContents);
            Stream str = new MemoryStream();
            str.Write(fileContentsBytes, 0, fileContentsBytes.Length);

            // add wrf file stream to sharepoint doc lib
            Guid reportLibID = new Guid(libGuid);
            SPDocumentLibrary reportDocLib = (SPDocumentLibrary)site.Lists[reportLibID];
            string reportLibRelPath = reportDocLib.RootFolder.ServerRelativeUrl;
            if (sOutFolderGuid != null)
            {
                Guid outFolderGuid = new Guid(sOutFolderGuid);
                SPFolder folder = site.GetFolder(outFolderGuid);
                reportLibRelPath = folder.ServerRelativeUrl;
            }
            string reportLibPath = SPContext.Current.Site.MakeFullUrl(reportLibRelPath);
            string reportPath = reportLibPath + "/" + filename;
            //string reportPath = "http://extrax.com/Assembly Framework/testNEW.reportdef";

            site.AllowUnsafeUpdates = true;
            site.Files.Add(reportPath, str, true);
            site.AllowUnsafeUpdates = false;
        }

        /// <summary>
        /// Adds a file to the specified doc lib.
        /// </summary>
        /// <param name="site"></param>
        /// <param name="docLib"></param>
        /// <param name="fileStream"></param>
        /// <param name="filename"></param>
        public static void AddFile(SPWeb site, SPDocumentLibrary docLib, Stream fileStream, string filename)
        {
            string reportLibRelPath = docLib.RootFolder.ServerRelativeUrl;
            string reportLibPath = SPContext.Current.Site.MakeFullUrl(reportLibRelPath);
            string reportPath = reportLibPath + "/" + filename;
            site.Files.Add(reportPath, fileStream, true);
        }
        /// <summary>
        /// <para>Gets the specified file from the specified web.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="webGuid"></param>
        /// <param name="fileGuid"></param>
        /// <returns></returns>
        public static SPFile GetSPFileFromSPWeb(Guid webGuid, Guid fileGuid)
        {
            SPWeb site = SPContext.Current.Site.OpenWeb(webGuid);
            return site.GetFile(fileGuid);
        }

        /// <summary>
        /// Gets a file from the current SPWeb.
        /// </summary>
        /// <param name="fileGuid"></param>
        /// <returns></returns>
        public static SPFile GetSPFileFromCurrentWeb(Guid fileGuid)
        {
            return SPContext.Current.Web.GetFile(fileGuid);
        }

        public static string getFileNameFromGuid(Guid documentGuid, Guid webGuid)
        {
            try
            {
                SPWeb web = ArrowUtils.getSPWeb(webGuid);
                SPFile docSPFile = ArrowUtils.GetSPFileFromSPWeb(webGuid, documentGuid);
                return SPContext.Current.Site.MakeFullUrl(docSPFile.ServerRelativeUrl);
            }
            catch (FileNotFoundException fnf)
            {
                return null;
            }
        }
        #endregion file utils

        #region data source utils
        /// <summary>
        /// Gets the list of objects for a select-type parameter.
        /// </summary>
        /// <param name="dataName">Data source name used in Windward tags.</param>
        /// <param name="dataDef">Database to get variables from.</param>
        /// <param name="selectCount">Query for number of variables.</param>
        /// <param name="selectAllowed">Query for variables.</param>
        /// <returns></returns>
        public static object[][] getSelectParameterObjects(string dataName, DataDefinition dataDef, string selectCount, string selectAllowed)
        {
            if (dataDef.IsXml)
                return DataSourceHandlerBase.EvaluateSelectAllowedXml(dataName, dataDef.ConnectionString, dataDef.UserName, dataDef.Password, selectCount, selectAllowed);
            else
            {
                string vendorName = dataDef.VendorName ?? string.Empty;
                string factoryClass = dataDef.FactoryClass ?? string.Empty;
                return DataSourceHandlerBase.EvaluateSelectAllowedAdo(dataName, dataDef.Description, dataDef.ConnectionString, vendorName, factoryClass, selectCount, selectAllowed);
            }
        }
        /// <summary>
        /// <para>Creates a KeyPairDictionary from the specified ReportDefinition and value list.
        /// Each key is a report variable/parameter name, and each value is the corresponding value.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="rptDef"></param>
        /// <param name="variableValues"></param>
        /// <returns></returns>
        internal static KeyPairDictionary CreateVarMap(ReportDefinition rptDef, string[] variableValues)
        {
            KeyPairDictionary map = new KeyPairDictionary();
            int numVars = ArrowUtils.GetNumVarsForDisplay(rptDef);
            int rptDefVarInd = 0;
            for (int i = 0; i < numVars; i++)
            {
                while (rptDef.variableNameArray[rptDefVarInd].Equals("WR_LOGIN") || rptDef.variableNameArray[rptDefVarInd].Equals("WR_USER"))
                {
                    rptDefVarInd++;
                }
                if (rptDefVarInd >= rptDef.variableNameArray.Length)
                {
                    //bugbug: this shouldn't happen
                    Trap.trap();
                    if (_log.IsDebugEnabled)
                        _log.Debug("rptDefVarInd >= rptDef.variableNameArray.Length returned true");
                }

                Object value;
                switch (rptDef.variableTypeArray[rptDefVarInd].ToLower())
                {
                    case "currency":
                        value = Convert.ToDecimal(variableValues[i]);
                        break;
                    case "date":
                        value = Convert.ToDateTime(variableValues[i]);
                        break;
                    case "integer":
                        value = Convert.ToInt64(variableValues[i]);
                        break;
                    case "number":
                        value = Convert.ToDouble(variableValues[i]);
                        break;
                    default:
                        value = variableValues[i];
                        break;
                }
                if (rptDef.variableNameArray[rptDefVarInd] != null && value != null && !map.Contains(rptDef.variableNameArray[rptDefVarInd]))
                    map.Add(rptDef.variableNameArray[rptDefVarInd], value);
                rptDefVarInd++;
            }
            return map;
        }
        /// <summary>
        /// <para>Adds the WR_USER and WR_LOGIN vars.</para>
        /// <para>Requires SPContext.</para>
        /// </summary>
        /// <param name="vars">The report variables to add to. Must not be null.</param>
        public static void AddCurrentUserInfoToReportVars(KeyPairDictionary vars)
        {
            const string WR_USER = "WR_USER";
            const string WR_LOGIN = "WR_LOGIN";

            SPUser currentUser = SPContext.Current.Web.CurrentUser;
            string userName = currentUser.Name ?? string.Empty;
            string userLogin = currentUser.LoginName ?? string.Empty;

            if (vars.Contains(WR_LOGIN))
                vars[WR_LOGIN] = userLogin;
            else
                vars.Add(WR_LOGIN, userLogin);

            if (vars.Contains(WR_USER))
                vars[WR_USER] = userName;
            else
                vars.Add(WR_USER, userName);
        }

        /// <summary>
        /// Gets the template variables, excluding the reserved vars WR_USER and WR_LOGIN, because those are
        /// set by Arrow, not by users. This is for displaying template vars for a user to enter values.
        /// </summary>
        /// <param name="tm"></param>
        /// <param name="log"></param>
        /// <returns>A list containing the template variables. Will be empty if there are no variables other than WR_USER and WR_LOGIN.</returns>
        public static IList<TemplateVariable> GetTemplateVarsForDisplay(TemplateMetrics tm, ILog log)
        {
            // copy all vars except WR_USER, WR_LOGIN
            // the user doesn't enter them, they are automatically filled when the report is generated

            const string WR_LOGIN = "WR_LOGIN";
            const string WR_USER = "WR_USER";
            List<TemplateVariable> tVars = new List<TemplateVariable>();
            foreach (TemplateVariable var in tm.TemplateVariables)
            {
                if (!var.Name.Equals(WR_LOGIN) && !var.Name.Equals(WR_USER))
                    tVars.Add(var);
            }
            return tVars;
        }

        /// <summary>
        /// Gets the template vars from TemplateMetrics.Vars, but excludes the reserved vars WR_USER and WR_LOGIN, because those are
        /// set by Arrow, not by users. This is for displaying template vars for a user to enter values.
        /// </summary>
        /// <param name="tm"></param>
        /// <returns></returns>
        public static string[] GetNeededVarNamesForDisplay(TemplateMetrics tm, ILog log)
        {
            const string WR_LOGIN = "WR_LOGIN";
            const string WR_USER = "WR_USER";
            int ind_WR_LOGIN = -1;
            int ind_WR_USER = -1;
            int neededVarsLen = 0;
            for (int i = 0; i < tm.Vars.Length; i++)
            {
                if (tm.Vars[i].Equals(WR_LOGIN))
                    ind_WR_LOGIN = i;
                else if (tm.Vars[i].Equals(WR_USER))
                    ind_WR_USER = i;
                else
                    neededVarsLen++;
            }
            string[] neededVars = new string[neededVarsLen];
            int indTmVar = 0;
            for (int i = 0; i < neededVarsLen; i++)
            {
                while (indTmVar == ind_WR_LOGIN || indTmVar == ind_WR_USER)
                {
                    indTmVar++;
                }

                if (indTmVar >= tm.Vars.Length)
                {
                    // bugbug: this shouldn't be possible, but this seemed a bit complex, so just in case
                    Trap.trap();
                    if (log.IsDebugEnabled)
                        log.Debug("indTmVar >= tm.Vars.Length returned true");
                    neededVars = tm.Vars;
                    break;
                }

                neededVars[i] = tm.Vars[indTmVar];
                indTmVar++;
            }
            return neededVars;
        }
        /// <summary>
        /// Gets the number of variables for display, excluding WR_USER and WR_LOGIN, because those are set by Arrow, not users.
        /// </summary>
        /// <param name="rai"></param>
        /// <returns></returns>
        public static int GetNumVarsForDisplay(ReportDefinition rai)
        {
            // set numVars
            const string WR_LOGIN = "WR_LOGIN";
            const string WR_USER = "WR_USER";
            int numVars = 0;
            for (int i = 0; i < rai.variableNameArray.Length; i++)
            {
                if (!(rai.variableNameArray[i].Equals(WR_LOGIN) || rai.variableNameArray[i].Equals(WR_USER)))
                    numVars++;
            }
            return numVars;
        }
        /// <summary>
        /// <para>Runs Report.ProcessData on each data source.</para>
        /// <para>Does not require SPContext.</para>
        /// </summary>
        /// <param name="reportGen"></param>
        /// <param name="rptDef"></param>
        /// <param name="dataDefArray"></param>
        /// <param name="map"></param>
        /// <param name="log"></param>
        internal static void ProcessReportGenDataSources(Report reportGen, ReportDefinition rptDef, DataDefinition[] dataDefArray, KeyPairDictionary map, ILog log)
        {
            if (log.IsDebugEnabled)
                log.DebugFormat("rdsi.Length={0}", dataDefArray.Length);

            for (int i = 0; i < dataDefArray.Length; i++)
            {
                DataDefinition dataDef = dataDefArray[i];
                IReportDataSource irds = dataDef.createIReportDataSource();
                if (map.Count > 0)
                    irds.Map = map;
                reportGen.ProcessData(irds, rptDef.dataSourceNameArray[i]);
            }
        }
        #endregion data source utils

        #region licensing methods
        /// <summary>
        /// Initializes WindwardReports engine and performs license enforcement.  
        /// Throws a LicenseException if the license is violated.
        /// </summary>
        /// <param name="webConfigPath">the physical path to the web config.  if null or empty, logging cannot be done.</param>
        public static void InitializeArrow(string webConfigPath)
        {
            try
            {
                // start the logger
                if (!string.IsNullOrEmpty(webConfigPath))
                {
                    log4net.Config.XmlConfigurator.Configure(new FileInfo(webConfigPath));
                }

                // read in license from web.config
                string strLicense = null;
                try
                {
                     strLicense = ArrowLicenseHandler.ReadLicense();
                }
                catch (Exception ex)
                {
                    LogFatalException(_log, ex);
                    throw new LicenseException(-1, "Error reading license.  Ensure the license is a valid license with no typos.");
                }

                // initialize the engine
                ArrowLicenseHandler.LoadJavaLicenseProperty(strLicense);
                Report.Init();
            }
            catch (LicenseException ex)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new Exception("There was an error initializing Arrow.", ex);
            }
        }
        #endregion licensing methods

        #region time utils
        /// <summary>
        /// Converts the specified UTC time to the time zone specified in the user's
        /// regional settings.  If the user has no regional settings, then server-local
        /// time is returned instead.
        /// </summary>
        /// <param name="user"></param>
        /// <param name="utcTime"></param>
        /// <returns></returns>
        public static DateTime UtcToUserTime(SPUser user, DateTime utcTime)
        {
            if (user.RegionalSettings != null)
                return user.RegionalSettings.TimeZone.UTCToLocalTime(utcTime);
            else
                return utcTime.ToLocalTime();
        }
        #endregion time utils

        #region IComparers for sorting SPWebs and SPListItems
        /// <summary>
        /// Compares SPWebs using string.Compare on SPWeb.Title.
        /// </summary>
        public class SPWebSortComparer : IComparer
        {
            #region IComparer Members

            public int Compare(object x, object y)
            {
                if (!(x is SPWeb) || !(y is SPWeb))
                    throw new ArgumentException("Cannot compare non-SPWeb objects using SPWebSortComparer");
                return string.Compare(((SPWeb)x).Title, ((SPWeb)y).Title);
            }

            #endregion
        }

        /// <summary>
        /// Compares SPListItems using string.Compare on SPListItem.Name.
        /// </summary>
        public class SPListItemComparer : IComparer
        {

            #region IComparer Members

            public int Compare(object x, object y)
            {
                if (!(x is SPListItem) || !(y is SPListItem))
                    throw new ArgumentException("Cannot compare non-SPListItem objects using SPListItemComparer");
                return string.Compare(((SPListItem)x).Name, ((SPListItem)y).Name);
            }

            #endregion
        }

        /// <summary>
        /// Compares SPFiles using string.Compare on SPFile.Name.
        /// </summary>
        public class SPFileComparer : IComparer
        {

            #region IComparer Members

            public int Compare(object x, object y)
            {
                if (!(x is SPFile) || !(y is SPFile))
                    throw new ArgumentException("Cannot compare non-SPFile objects using SPFileComparer");
                return string.Compare(((SPFile)x).Name, ((SPFile)y).Name);
            }

            #endregion
        }
        #endregion IComparers for sorting

        #region Logging

        /// <summary>
        /// Logs the specified exception as a fatal level log item.
        /// </summary>
        /// <param name="ex"></param>
        public static void LogFatalException(ILog log, Exception ex)
        {
            if (log.IsFatalEnabled)
                log.Fatal("Fatal exception occurred.", ex);
        }

        /// <summary>
        /// Logs the specified exception as a warn level log item.
        /// </summary>
        /// <param name="ex"></param>
        public static void LogWarnException(ILog log, Exception ex)
        {
            if (log.IsWarnEnabled)
                log.Warn("Nonfatal exception occurred.", ex);
        }

        #endregion Logging

        #region tableCreationFunctions
        public static Table createEmptyTable(int numRows, int numCols, int rowHeight, int rowWidth)
        {
            Table t = new Table();
            t.Width = Unit.Percentage(100);
            t.BorderStyle = BorderStyle.None;

            for (int i = 0; i < numRows; i++)
            {
                TableRow r = new TableRow();
                r.BorderStyle = BorderStyle.None;
                for (int j = 0; j < numCols; j++)
                {
                    TableCell c = new TableCell();
                    c.Height = rowHeight;
                    c.CssClass = ".ms-formlabel";
                    c.VerticalAlign = VerticalAlign.Top;
                    //c.BorderWidth = Unit.Pixel(0);
                    c.BorderStyle = BorderStyle.None;
                    r.Cells.Add(c);

                }
                r.Width = Unit.Percentage(100);
                t.Rows.Add(r);
            }
            return t;
        }
        public static Table createEmptyTableStatic(int numRows, int numCols, int rowHeight, int rowWidth)
        {
            Table t = new Table();
            for (int i = 0; i < numRows; i++)
            {
                TableRow r = new TableRow();

                for (int j = 0; j < numCols; j++)
                {
                    TableCell c = new TableCell();
                    c.CssClass = ".ms-formlabel";
                    c.Height = rowHeight;
                    c.VerticalAlign = VerticalAlign.Top;
                    r.Cells.Add(c);
                }
                r.Width = Unit.Pixel(rowWidth);
                t.Rows.Add(r);
            }
            return t;
        }
        public static Table makeTableAlternating(Table tbl)
        {
            Table toReturn = tbl;
            for (int i = 0; i < toReturn.Rows.Count; i = i + 2)
            {
                toReturn.Rows[i].CssClass = "ms-alternating";
            }
            return toReturn;
        }
        public static Table makeTableAlternatingOffset(Table tbl)
        {
            Table toReturn = tbl;
            for (int i = 1; i < toReturn.Rows.Count; i = i + 2)
            {
                toReturn.Rows[i].CssClass = "ms-alternating";
            }
            return toReturn;
        }
        /// <summary>
        /// Creates a Table control for the vars of the Run a Report menu action.
        /// </summary>
        /// <param name="rai"></param>
        /// <param name="numVars"></param>
        /// <param name="variableValueTextBoxArray"></param>
        /// <param name="variableValueDropDownListArray"></param>
        /// <returns></returns>
        public static Table createVarTable_runreport(ReportDefinition rai, int numVars, out TextBox[] variableValueTextBoxArray, out DropDownList[] variableValueDropDownListArray)
        {
            Label varName = new Label();
            varName.Text = "Variable Name";
            varName.Width = Unit.Pixel(200);
            varName.ApplyStyle(ArrowUtils.normalBoldStyle());
            Label required = new Label();
            required.Text = "Required ?";
            required.Width = Unit.Pixel(180);
            required.ApplyStyle(ArrowUtils.normalBoldStyle());
            Label type = new Label();
            type.Text = "Type";
            type.Width = Unit.Pixel(150);
            type.ApplyStyle(ArrowUtils.normalBoldStyle());
            Label variableVal = new Label();
            variableVal.Text = "Variable Value";
            variableVal.Width = Unit.Pixel(150);
            variableVal.ApplyStyle(ArrowUtils.normalBoldStyle());

            // set numVars
            const string WR_LOGIN = "WR_LOGIN";
            const string WR_USER = "WR_USER";
            int ind_WR_LOGIN = -1;
            int ind_WR_USER = -1;
            for (int i = 0; i < rai.variableNameArray.Length; i++)
            {
                if (rai.variableNameArray[i].Equals(WR_LOGIN))
                    ind_WR_LOGIN = i;
                else if (rai.variableNameArray[i].Equals(WR_USER))
                    ind_WR_USER = i;
            }

            Table t = makeTableAlternating(createEmptyTableStatic(1 + numVars, 4, 30, 800));
            t.Width = Unit.Percentage(100);

            t.Rows[0].Cells[0].Controls.Add(varName);
            t.Rows[0].Cells[1].Controls.Add(required);
            t.Rows[0].Cells[2].Controls.Add(type);
            t.Rows[0].Cells[3].Controls.Add(variableVal);

            Label[] variableNameLabelArray = new Label[numVars];
            Label[] variableRequiredLabelArray = new Label[numVars];
            Label[] variableTypeLabelArray = new Label[numVars];
            variableValueTextBoxArray = new TextBox[numVars];
            variableValueDropDownListArray = new DropDownList[numVars];

            for (int j = 0; j < numVars; j++)
            {
                variableNameLabelArray[j] = new Label();
                t.Rows[1 + j].Cells[0].Controls.Add(variableNameLabelArray[j]);

                variableRequiredLabelArray[j] = new Label();
                t.Rows[1 + j].Cells[1].Controls.Add(variableRequiredLabelArray[j]);

                variableTypeLabelArray[j] = new Label();
                t.Rows[1 + j].Cells[2].Controls.Add(variableTypeLabelArray[j]);

                variableValueTextBoxArray[j] = new TextBox();
                t.Rows[1 + j].Cells[3].Controls.Add(variableValueTextBoxArray[j]);

                variableValueDropDownListArray[j] = new DropDownList();
                t.Rows[1 + j].Cells[3].Controls.Add(variableValueDropDownListArray[j]);
            }
            
            // hide all the controls by default
            for (int i = 0; i < variableNameLabelArray.Length; i++)
            {
                variableNameLabelArray[i].Visible = false;
                variableRequiredLabelArray[i].Visible = false;
                variableTypeLabelArray[i].Visible = false;
                variableValueTextBoxArray[i].Visible = false;
                variableValueDropDownListArray[i].Visible = false;
            }

            // go through the ReportDefinition, and "enable" a control for each variable
            if (rai != null)
            {
                #region enable variable input controls
                int raiVarInd = 0;
                //for (int i = 0; i < rai.variableNameArray.Length; i++)
                for (int i = 0; i < numVars; i++)
                {
                    while (raiVarInd == ind_WR_LOGIN || raiVarInd == ind_WR_USER)
                    {
                        raiVarInd++;
                    }
                    if (raiVarInd >= rai.variableNameArray.Length)
                    {
                        //bugbug: this shouldn't happen
                        Trap.trap();
                        if (_log.IsDebugEnabled)
                            _log.Debug("raiVarInd >= rai.variableNameArray.Length returned true");
                    }

                    variableNameLabelArray[i].Visible = true;
                    variableNameLabelArray[i].Text = rai.variableNameArray[raiVarInd];

                    variableRequiredLabelArray[i].Visible = true;
                    variableRequiredLabelArray[i].Text = rai.variableRequiredArray[raiVarInd];

                    variableTypeLabelArray[i].Visible = true;
                    variableTypeLabelArray[i].Text = rai.variableTypeArray[raiVarInd];

                    if (variableTypeLabelArray[i].Text.ToLower() == "select")
                    {

                        //do select and add items;
                        string nameToSearch = rai.variableDataNameArray[raiVarInd];
                        DataDefinition[] datasources = rai.getDataSources();
                        DataDefinition dataDef = null;
                        for (int j = 0; j < rai.dataSourceNameArray.Length; j++)
                        {
                            if (rai.dataSourceNameArray[j] == nameToSearch)
                            {
                                dataDef = datasources[j];
                                break;
                            }
                        }
                        if (dataDef == null)
                            throw new FileNotFoundException(string.Format("Could not find datadef entry named {0}.", nameToSearch));

                        object[][] queryResult = ArrowUtils.getSelectParameterObjects(rai.variableDataNameArray[raiVarInd], dataDef, rai.variableSelectCountArray[raiVarInd], rai.variableSelectAllArray[raiVarInd]);
                        if (queryResult.Length > 25)
                        {
                            //display a textbox instead of a drop down, do validatio on next button click
                            variableValueTextBoxArray[i].Visible = true;
                            variableValueTextBoxArray[i].Text = rai.variableDefaultArray[raiVarInd];
                        }
                        else
                        {
                            variableValueDropDownListArray[i].Visible = true;
                            foreach (object[] ob in queryResult)
                            {
                                string input = ob[0].ToString();
                                variableValueDropDownListArray[i].Items.Add(new ListItem(input, input));
                            }
                            if (variableValueDropDownListArray[i].Items.Count == 0)
                            {
                                variableValueDropDownListArray[i].Items.Add(new ListItem("<none>", ""));
                            }
                            //set default value
                            string defaultValue = rai.variableDefaultArray[raiVarInd];
                            for (int j = 0; j < variableValueDropDownListArray[i].Items.Count; j++)
                            {
                                if (variableValueDropDownListArray[i].Items[j].Value == defaultValue)
                                {
                                    variableValueDropDownListArray[i].SelectedIndex = j;
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        variableValueTextBoxArray[i].Visible = true;
                        variableValueTextBoxArray[i].Text = rai.variableDefaultArray[raiVarInd];
                    }
                    if ("True".Equals(rai.variableHiddenArray[raiVarInd]))
                    {
                        variableTypeLabelArray[i].Visible = false;
                        variableValueDropDownListArray[i].Visible = false;
                        variableValueTextBoxArray[i].Visible = false;
                        variableNameLabelArray[i].Visible = false;
                        variableRequiredLabelArray[i].Visible = false;
                        variableNameLabelArray[i].Visible = false;
                    }

                    raiVarInd++;
                }
                #endregion enable variable input controls
            }

            // return the variable input form
            return t;
        }

        public static Table createVarTable_runreport(ReportDefinition rai, int numVars, out TextBox[] variableValueTextBoxArray, out DropDownList[] variableValueDropDownListArray, out Label[] variableNameLabelArray, out Label[] variableRequiredLabelArray, out Label[] variableTypeLabelArray)
        {
            Label varName = new Label();
            varName.Text = "Variable Name";
            varName.Width = Unit.Pixel(200);
            varName.ApplyStyle(ArrowUtils.normalBoldStyle());
            Label required = new Label();
            required.Text = "Required ?";
            required.Width = Unit.Pixel(180);
            required.ApplyStyle(ArrowUtils.normalBoldStyle());
            Label type = new Label();
            type.Text = "Type";
            type.Width = Unit.Pixel(150);
            type.ApplyStyle(ArrowUtils.normalBoldStyle());
            Label variableVal = new Label();
            variableVal.Text = "Variable Value";
            variableVal.Width = Unit.Pixel(150);
            variableVal.ApplyStyle(ArrowUtils.normalBoldStyle());

            Table t = makeTableAlternating(createEmptyTableStatic(1 + numVars, 4, 30, 800));
            t.Width = Unit.Percentage(100);

            t.Rows[0].Cells[0].Controls.Add(varName);
            t.Rows[0].Cells[1].Controls.Add(required);
            t.Rows[0].Cells[2].Controls.Add(type);
            t.Rows[0].Cells[3].Controls.Add(variableVal);

            variableNameLabelArray = new Label[numVars];
            variableRequiredLabelArray = new Label[numVars];
            variableTypeLabelArray = new Label[numVars];
            variableValueTextBoxArray = new TextBox[numVars];
            variableValueDropDownListArray = new DropDownList[numVars];

            for (int j = 0; j < numVars; j++)
            {
                variableNameLabelArray[j] = new Label();
                t.Rows[1 + j].Cells[0].Controls.Add(variableNameLabelArray[j]);

                variableRequiredLabelArray[j] = new Label();
                t.Rows[1 + j].Cells[1].Controls.Add(variableRequiredLabelArray[j]);

                variableTypeLabelArray[j] = new Label();
                t.Rows[1 + j].Cells[2].Controls.Add(variableTypeLabelArray[j]);

                variableValueTextBoxArray[j] = new TextBox();
                t.Rows[1 + j].Cells[3].Controls.Add(variableValueTextBoxArray[j]);

                variableValueDropDownListArray[j] = new DropDownList();
                t.Rows[1 + j].Cells[3].Controls.Add(variableValueDropDownListArray[j]);
            }

            // hide all the controls by default
            for (int i = 0; i < variableNameLabelArray.Length; i++)
            {
                variableNameLabelArray[i].Visible = false;
                variableRequiredLabelArray[i].Visible = false;
                variableTypeLabelArray[i].Visible = false;
                variableValueTextBoxArray[i].Visible = false;
                variableValueDropDownListArray[i].Visible = false;
            }

            // return the variable input form
            return t;
        }



        #endregion
    }
}
